package com.example.lotterysystem.service.mq;

import cn.hutool.core.date.DateUtil;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.MailUtil;
import com.example.lotterysystem.common.utils.SMSUtil;
import com.example.lotterysystem.controller.param.DrawPrizeParam;
import com.example.lotterysystem.dao.dataobject.ActivityPrizeDO;
import com.example.lotterysystem.dao.dataobject.WinningRecordDO;
import com.example.lotterysystem.dao.mapper.ActivityPrizeMapper;
import com.example.lotterysystem.dao.mapper.WinningRecordMapper;
import com.example.lotterysystem.service.DrawPrizeService;
import com.example.lotterysystem.service.activitystatus.ActivityStatusManager;
import com.example.lotterysystem.service.dto.ConvertActivityStatusDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import com.example.lotterysystem.service.enums.ActivityUserStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.example.lotterysystem.common.config.DirectRabbitConfig.QUEUE_NAME;

/**
 * @auther falls_vc
 * description:
 * @date 2025/9/12  16:02
 */
@Component
@RabbitListener(queues = QUEUE_NAME)
public class MqReceiver {

    private static final Logger logger = LoggerFactory.getLogger(MqReceiver.class);

    @Autowired
    private DrawPrizeService drawPrizeService;
    @Autowired
    private ActivityStatusManager activityStatusManager;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private MailUtil mailUtil;
    @Autowired
    private SMSUtil smsUtil;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private WinningRecordMapper winningRecordMapper;

    @RabbitHandler
    public void process(Map<String,String> message) {
        // 成功接收队列里的消息
        logger.info("MQ成功收到消息，message：{}",
                JacksonUtil.writeValueAsString(message));
        String paramString = message.get("messageData");
        DrawPrizeParam param = JacksonUtil.readValue(paramString,DrawPrizeParam.class);
        // 处理抽奖流程

        try {

            // 校验抽奖请求是否有效
            if(!drawPrizeService.checkDrawPrizeParam(param)) {
                return;
            }

            // 状态扭转处理
            statusConvert(param);
            // 保存中奖者名单
            List<WinningRecordDO> winningRecordDOList =
                    drawPrizeService.saveWinnerRecords(param);

            // 通知中奖者 邮箱 （短信服务有问题 暂时无法发送短信）
            syncExecute(winningRecordDOList);

        } catch (ServiceException e) {
            logger.error("处理MQ 消息异常！{}：{}",e.getCode(),e.getMessage(),e);
            // 如果异常，需要保证事务一致性（回滚） 抛出异常
            rollback(param);
            // 抛出异常 消息重试 （解决异常 ）
            throw e;
        } catch (Exception e) {
            logger.error("处理MQ 消息异常！{}",e);
            // 如果异常，需要保证事务一致性（回滚） 抛出异常
            rollback(param);
            // 抛出异常 消息重试 （解决异常 ）
            throw e;
        }
    }

    private void rollback(DrawPrizeParam param) {
        // 1. 回滚状态：活动、人员、奖品
        if(!statusNeedRollback(param)) {
            // 不需要
            return;
        }
        // 需要 :回滚
        rollbackStatus(param);

        // 2. 回滚中奖者名单
        if(!winnerNeedRollback(param)) {
            // 不需要
            return;
        }
        // 需要
        rollbackWinner(param);

    }

    private void rollbackWinner(DrawPrizeParam param) {
        drawPrizeService.deleteRecords(param.getActivityId(),param.getPrizeId());
    }

    private boolean winnerNeedRollback(DrawPrizeParam param) {
        int count = winningRecordMapper.countByAPId(param.getActivityId(), param.getPrizeId());
        return count > 0;
    }

    /**
     * 恢复相关状态
     *
     * @param param
     */
    private void rollbackStatus(DrawPrizeParam param) {
        ConvertActivityStatusDTO convertActivityStatusDTO = new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(param.getActivityId());
        convertActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.RUNNING);
        convertActivityStatusDTO.setPrizeId(param.getPrizeId());
        convertActivityStatusDTO.setTargetPrizeStatus(ActivityPrizeStatusEnum.INIT);
        convertActivityStatusDTO.setUserIds(
                param.getWinnerList().stream()
                        .map(DrawPrizeParam.Winner::getUserId)
                        .collect(Collectors.toList())
        );
        convertActivityStatusDTO.setTargetUserStatus(ActivityUserStatusEnum.INIT);

        // 涉及恢复工作 ActivityStatusManager
        activityStatusManager.rollbackHandlerEvent(convertActivityStatusDTO);
    }

    private boolean statusNeedRollback(DrawPrizeParam param) {
        // 状态转换已经保证事务一致性  @Transactional
        // 现在判断一个状态是否状态更改就能判断是否需要回滚
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(),param.getPrizeId());
        // true 已经扭转需回滚
        return activityPrizeDO.getStatus()
                .equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name());

    }

    /**
     * 并发处理抽奖后续
     *
     * @param winningRecordDOList
     */
    private void syncExecute(List<WinningRecordDO> winningRecordDOList) {
        // 通过线程池 threadPoolTaskExecutor
        // 短信服务
//        threadPoolTaskExecutor.execute(()->sendMessage(winnerRecordDOList));

        // 邮件通知
        threadPoolTaskExecutor.execute(()->sendMail(winningRecordDOList));
    }

    /**
     * 发邮件
     *
     * @param winningRecordDOList
     */
    private void sendMail(List<WinningRecordDO> winningRecordDOList) {
        if(CollectionUtils.isEmpty(winningRecordDOList)) {
            logger.info("中奖列表为空！不用发邮件 ");
            return;
        }
        for (WinningRecordDO winningRecordDO : winningRecordDOList) {
            // Hi,胡一博。恭喜你在抽奖活动活动中获得二等奖:吹风机。获奖奖时间为18:18:44,请尽快领取您的奖励
            String context = "Hi，" + winningRecordDO.getWinnerName() + "。恭喜你在"
                    + winningRecordDO.getActivityName() + "活动中获得"
                    + ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()).getMessage()
                    + "：" + winningRecordDO.getPrizeName() + "。获奖时间为"
                    + DateUtil.formatTime(winningRecordDO.getWinningTime()) + "，请尽快领 取您的奖励！";
            mailUtil.sendMail(winningRecordDO.getWinnerEmail(),
                    "中奖通知", context);
        }
    }

    /**
     * 发短信
     * @param winningRecordDOList
     */

    private void sendMessage(List<WinningRecordDO> winningRecordDOList) {
        if(CollectionUtils.isEmpty(winningRecordDOList)) {
            logger.info("中奖列表为空！不用发邮件 ");
            return;
        }

        for (WinningRecordDO winningRecordDO: winningRecordDOList) {
            Map<String,String> map = new HashMap<>();
            /**
             *  key-value 值
             *  按模版 put
             */

            smsUtil.sendMessage("模版码 这里无法申请使用",winningRecordDO.getWinnerPhoneNumber().getValue(),
                JacksonUtil.writeValueAsString(map)
            );
        }
    }

    /**
     * 状态扭转
     * @param param
     */
    private void statusConvert(DrawPrizeParam param) {
        ConvertActivityStatusDTO convertActivityStatusDTO = new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(param.getActivityId());
        convertActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.COMPLETED);
        convertActivityStatusDTO.setPrizeId(param.getPrizeId());
        convertActivityStatusDTO.setTargetPrizeStatus(ActivityPrizeStatusEnum.COMPLETED);
        convertActivityStatusDTO.setUserIds(
                param.getWinnerList().stream()
                     .map(DrawPrizeParam.Winner::getUserId)
                     .collect(Collectors.toList())
        );
        convertActivityStatusDTO.setTargetUserStatus(ActivityUserStatusEnum.COMPLETED);

        activityStatusManager.handlerEvent(convertActivityStatusDTO);
    }
//    private void statusConvert(DrawPrizeParam param) {
//        // 活动: RUNNING --> COMPLETED OR RUNNING
//        // 奖品：INIT --> COMPLETED
//        // 人员列表: INIT --> COMPLETED
//
//        // 1. 扭转奖品状态
//        // 查询奖品信息
//        // 如果不是COMPLETED  要扭转
//
//        // 2. 扭转人员状态
//        // 查询人员信息
//        // 如果不是COMPLETED  要扭转
//
//        // 3. 扭转活动状态 （必须在扭转活动状态后）
//        // 查询活动信息
//        // 如果不是COMPLETED  要扭转
//
//        // 更新活动信息缓存（redis）
//    }

}
